//
// Created by LiuYou on 2021/7/19.
//

/// \brief Copy Control
/// big-three: copy constructor/copy assignment operator/destructor
/// big-five: copy constructor/copy assignment operator/destructor/move constructor/move assignment operator
///
/// 有些问题是不需要细究的, 因为没有意义, 也没有营养。不要钻牛角尖。
///
/*
 * 三/五法则
 *
 * = default
 * = delete
 *
 * 类的行为像一个值, 意味着它应该也有自己的状态。当我们拷贝一个像值的对象时, 副本和原对象是完全独立的。改变副本不会对原对象产生任何影响。
 * 类的行为像一个指针, 意味着共享状态。当我们拷贝一个这种类的对象时, 副本和原对象的一个或者一些字段(指针类型)使用相同的底层数据。改变副本也会改变原对象。
 *
 * 标准库类中:
 * 标准库容器和string类的行为像一个值。
 * shared_ptr的行为像一个指针。
 * IO类型和unique_ptr不允许拷贝或赋值, 因此它们的行为即不像值也不像指针。
 *
 * 类管理的资源就是类的字段。
 *
 * 令一个类展现类似指针的行为最好方法是使用 std::shared_ptr 来管理类中的资源。
 * 拷贝(或赋值)一个 shared_ptr 会拷贝(或赋值) shared_ptr 所指向的指针。shared_ptr对象自己记录有多少用户共享它所指向的对象。当没有用户使用对象时, shared_ptr对象自己会负责释放资源。
 *
 * 但是有时我们希望直接管理资源。在这种情况下, 使用引用计数就很有用了。
 *
 * 设计引用计数来使 HasPtr 类的行为像一个指针, 重构之前的 HasPtrLikeValue 为 HasPtrLikePtrUsePointer。
 *
 * 使用 shared_ptr 来管理类中的资源, 这比自己写的引用计数方便很多。
 *
 * 除了定义拷贝控制成员, 管理资源的类通常还定义一个名为 swap 的函数。
 * 对于那些与重排元素顺序的算法(排序算法)一起使用的类, 定义swap是非常重要的。
 *
 * 定义swap的类通常用swap来定义它们的拷贝赋值运算符(copy assignment operator)。这使用了一种名为拷贝并交换(copy and swap)的技术。
 * 这种技术将左侧运算对象与右侧运算对象的一个副本进行交换。
 * 拷贝并交换这项技术的有趣指出是它自动处理了自我赋值情况且天然就是异常安全的。之后写copy assignment operator就使用这种写法。
 *
 * 动态管理类。
 * 在重新分配内存的过程中移动而不是拷贝元素。
 *
 *
 *
 * 对象移动。
 * 很多情况下都会发生对象的拷贝, 在其中某些情况下, 对象拷贝后就立刻被销毁了。在这种情况下, 移动而非拷贝对象会大幅度提升性能。
 *
 * 使用移动而不是拷贝的另一个原因是IO类或unique_ptr这样的类。这些类都包含不能被共享的资源(如指针或IO缓冲)。因此, 这些类型的对象不能够拷贝但是可以移动!
 * 标准库容器、std::string和std::shared_ptr类即支持移动也支持拷贝。IO类和unique_ptr类可以移动但是不能拷贝。
 *
 * 为了支持移动操作, 引入了右值引用(rvalue reference)。右值引用就是必须绑定在右值的引用(即只能绑定到一个将要销毁的对象)。
 * 类似任何引用, 一个右值引用也不过是某个对象的另一个名字而已。
 *
 * std::move();
 *
 * 移动构造函数和移动赋值运算符:
 * 类似std::string类(或者其它标准库类), 如果我们自己的类也同时支持移动和拷贝, 那么也能从中受益。为了让我们自己的类支持移动操作, 需要为这个类
 * 编写移动构造函数和移动赋值运算符。注意这两个函数都要加上 noexcept 关键字。
 *
 * 如果一个类既有移动构造函数, 也有拷贝构造函数, 编译器使用普通的函数匹配规则来确定使用哪个构造函数。P477
 *
 * 拷贝并交换技术在移动赋值运算符中的使用。非常精妙, 这个赋值运算符将会是既是移动赋值运算符也是拷贝赋值运算符。
 *
 * 三/五法则: 如果一个类定义了Big-Five中的任何一个, 它就应该定义所有五个操作。
 *
 * 移动迭代器:
 * 一般来说, 一个迭代器的解引用运算符返回一个指向元素的左值。而移动迭代器的解引用运算符生成一个右值引用。
 * 通过调用标准库的 std::make_move_iterator() 函数将一个普通的迭代器转换为一个移动迭代器。
 *
 * 最后建议: 不要随意使用移动操作。那什么时候使用呢, 在敏捷开发完成后, 不断的重构阶段优化代码的时候小心的使用std::move()优化重构代码。这才是
 * 移动操作的使用之道。其实这句话对其它优化来说也是正确的, 没有人可以一下子写出非常优秀的代码, 都是不断重构优化来的。
 *
 * 除了构造函数和赋值运算符之外, 如果一个成员函数同时提供了拷贝和移动版本, 它也能从中受益。
 * 拷贝版本从参数拷贝数据, 移动版本从参数窃取数据。
 *
 * 右值和左值引用成员函数:
 * 引用限定符可以是&或者&&, 分别指出this可以指向一个左值或右值。
 * 这个在C++中使用比较少。我到现在为止没有遇到过。
 *
 * 分配了内存或其它资源的类几乎总是需要定义Big-Five来管理分配的资源。
 *
 * 拷贝控制结束。
 */



#include <cstdlib>
#include <iostream>
#include "HasPtrLikeValue.hpp"
#include "HasPtrLikePtrUsePointer.hpp"
#include "HasPtrLikePtrUseSmartPointer.hpp"


using ResourceManagement::HasPtrLikeValue;
using ResourceManagement::HasPtrLikePtrUsePointer;
using ResourceManagement::HasPtrLikePtrUseSmartPointer;


/*
HasPtrLikeValue.cpp:41:15: warning: ‘std::ostream& ResourceManagement::operator<<(std::ostream&, const ResourceManagement::HasPtrLikeValue&)’ has not been declared within ‘ResourceManagement’
 std::ostream& ResourceManagement::operator<<(std::ostream& os, const ResourceManagement::HasPtrLikeValue& value) {
               ^~~~~~~~~~~~~~~~~~
In file included from HasPtrLikeValue.cpp:5:
HasPtrLikeValue.hpp:16:30: note: only here as a ‘friend’
         friend std::ostream& operator<<(std::ostream& os, const HasPtrLikeValue& value);
                              ^~~~~~~~
将stream output function写到类内就不会有这样的警告了。
 */
int main() {
    HasPtrLikeValue hasPtrLikeValue( "Go Move" );
    std::cout << "hasPtrLikeValue: " << hasPtrLikeValue << std::endl;
    std::cout << "sizeof (hasPtrLikeValue): " << sizeof( hasPtrLikeValue ) << std::endl;

    HasPtrLikeValue hasPtrLikeValue1;
    std::cout << "hasPtrLikeValue1: " << hasPtrLikeValue1 << std::endl;
    std::cout << "sizeof (hasPtrLikeValue1): " << sizeof( hasPtrLikeValue1 ) << std::endl;

    hasPtrLikeValue1 = hasPtrLikeValue1;
    std::cout << "hasPtrLikeValue1: " << hasPtrLikeValue1 << std::endl;
    std::cout << "sizeof (hasPtrLikeValue1): " << sizeof( hasPtrLikeValue1 ) << std::endl;

    std::cout << "================================================================" << std::endl;


    {
//        HasPtrLikePtrUsePointer hasPtrLikePtr;
//        std::cout << "hasPtrLikePtr: " << hasPtrLikePtr << std::endl;
//        std::cout << "sizeof (hasPtrLikePtr): " << sizeof( hasPtrLikePtr ) << std::endl;
//
//        HasPtrLikePtrUsePointer hasPtrLikePtr1( hasPtrLikePtr );
//        std::cout << "hasPtrLikePtr: " << hasPtrLikePtr << std::endl;
//        std::cout << "sizeof (hasPtrLikePtr): " << sizeof( hasPtrLikePtr ) << std::endl;
//        std::cout << "hasPtrLikePtr1: " << hasPtrLikePtr1 << std::endl;
//        std::cout << "sizeof (hasPtrLikePtr1): " << sizeof( hasPtrLikePtr1 ) << std::endl;
//
//        HasPtrLikePtrUsePointer hasPtrLikePtr2( hasPtrLikePtr );
//        // 测试结果 这三个对象的referenceCount字段都是3。
//        std::cout << "hasPtrLikePtr: " << hasPtrLikePtr << std::endl;
//        std::cout << "sizeof (hasPtrLikePtr): " << sizeof( hasPtrLikePtr ) << std::endl;
//        std::cout << "hasPtrLikePtr1: " << hasPtrLikePtr1 << std::endl;
//        std::cout << "sizeof (hasPtrLikePtr1): " << sizeof( hasPtrLikePtr1 ) << std::endl;
//        std::cout << "hasPtrLikePtr2: " << hasPtrLikePtr2 << std::endl;
//        std::cout << "sizeof (hasPtrLikePtr2): " << sizeof( hasPtrLikePtr2 ) << std::endl;
    }

    {
        HasPtrLikePtrUsePointer hasPtrLikePtr( "Gan" );
        HasPtrLikePtrUsePointer hasPtrLikePtr1;
        std::cout << "hasPtrLikePtr: " << hasPtrLikePtr << std::endl;
        std::cout << "sizeof (hasPtrLikePtr): " << sizeof( hasPtrLikePtr ) << std::endl;
        std::cout << "hasPtrLikePtr1: " << hasPtrLikePtr1 << std::endl;
        std::cout << "sizeof (hasPtrLikePtr1): " << sizeof( hasPtrLikePtr1 ) << std::endl;

        hasPtrLikePtr1 = hasPtrLikePtr;
        std::cout << "hasPtrLikePtr: " << hasPtrLikePtr << std::endl;
        std::cout << "sizeof (hasPtrLikePtr): " << sizeof( hasPtrLikePtr ) << std::endl;
        std::cout << "*hasPtrLikePtr.getReferenceCount(): " << *hasPtrLikePtr.getReferenceCount() << std::endl;
        std::cout << "*hasPtrLikePtr1.getReferenceCount(): " << *hasPtrLikePtr1.getReferenceCount() << std::endl;
        /// \brief hasPtrLikePtr1之前管理的那块内存, 即之前你管理的资源已经被释放掉了, 再也找不回来了。
        std::cout << "hasPtrLikePtr1: " << hasPtrLikePtr1 << std::endl;
        std::cout << "sizeof (hasPtrLikePtr1): " << sizeof( hasPtrLikePtr1 ) << std::endl;
    }


    std::cout << "================================================================" << std::endl;


    {
        HasPtrLikePtrUseSmartPointer hasPtrLikePtrUseSmartPointer;
        std::cout << "hasPtrLikePtrUseSmartPointer: " << hasPtrLikePtrUseSmartPointer << std::endl;
        std::cout << "sizeof (hasPtrLikePtrUseSmartPointer): " << sizeof( hasPtrLikePtrUseSmartPointer ) << std::endl;
        HasPtrLikePtrUseSmartPointer hasPtrLikePtrUseSmartPointer1( "Chong" );
        std::cout << "hasPtrLikePtrUseSmartPointer1: " << hasPtrLikePtrUseSmartPointer1 << std::endl;
        std::cout << "sizeof (hasPtrLikePtrUseSmartPointer1): " << sizeof( hasPtrLikePtrUseSmartPointer1 ) << std::endl;

        hasPtrLikePtrUseSmartPointer = hasPtrLikePtrUseSmartPointer1;
        /// \brief 同样hasPtrLikePtrUseSmartPointer内的指针指针管理的之前的资源已经释放掉了, 再也找不回来了。
        std::cout << "hasPtrLikePtrUseSmartPointer: " << hasPtrLikePtrUseSmartPointer << std::endl;
        std::cout << "sizeof (hasPtrLikePtrUseSmartPointer): " << sizeof( hasPtrLikePtrUseSmartPointer ) << std::endl;
        std::cout << "hasPtrLikePtrUseSmartPointer1: " << hasPtrLikePtrUseSmartPointer1 << std::endl;
        std::cout << "sizeof (hasPtrLikePtrUseSmartPointer1): " << sizeof( hasPtrLikePtrUseSmartPointer1 ) << std::endl;
    }


    std::exit( 0 );
}